﻿using System;
using System.Diagnostics;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace N2.Web.UI.WebControls
{
	using ComponentModel;
	using Workflow;
	using Workflow.Items;
	using Definitions;
	
	/// <summary>
	/// 
	/// </summary>
	/// <see cref="N2.Web.UI.WebControls.ItemEditorList"/>
	public class WorkflowActionToolbar : CompositeControl, INamingContainer
	{
		#region Candidate properties for IoC
		
		readonly IWorkflowProvider WorkflowProvider;
		readonly IDefinitionManager Definitions;
		
		#endregion Candidate properties for IoC

		#region Workflow Properties
		
		IItemState m_initialState;
		/// <summary>
		/// A wrapper around initial item state, guaranteed to be not null.
		/// </summary>
		IItemState CurrentState {
			get {
				return this.m_initialState
					?? (this.m_initialState = this.WorkflowProvider.GetCurrentState(this.ParentItem));
			}
		}

		//TODO eliminate after the cause for a 'failed to lazily initialize a collection' is resolved
		StateDefinition CurrentStateDefinition {
			get {
				return
					this.CurrentState is ItemState
						? ((ItemState)this.CurrentState).Definition
						: this.CurrentState.Definition is ContentItem
							? N2.Context.Persister.Get<StateDefinition>((this.CurrentState.Definition as ContentItem).ID)
							: null;
			}
		}

		#endregion Workflow Properties

		#region Fields

		private DropDownList types;
		readonly ViewStateCurrentItemPropertyHolder m_parentPropertyHolder;
		readonly ViewStateCurrentItemPropertyHolder m_selectedActionHolder;
		FieldSet fs;
		
		PlaceHolder itemEditorsContainer;
		PlaceHolder actionSelectorContainer;
		#endregion

		#region ctor

		public WorkflowActionToolbar()
		{
			CssClass = "itemListEditor";
			this.m_parentPropertyHolder = new ViewStateCurrentItemPropertyHolder(this.ViewState);
			this.m_selectedActionHolder = new ViewStateCurrentItemPropertyHolder(this.ViewState);

			//TODO throw exception if NULL: this control can only work
			// with ContentItem-based provider
			this.WorkflowProvider = N2.Context.Current.Resolve<IWorkflowProvider>() as WorkflowProvider;
			
			this.Definitions = N2.Context.Definitions;
		}

		#endregion ctor

		#region Properties

		public ItemEditor ItemEditor { get; protected set; }

		public bool WorkflowActionPerformed {
			get { return null != this.SelectedAction; }
		}

		protected Type NewStateType {
			get { return this.SelectedAction.LeadsTo.StateType; }
		}

		/// <summary>
		/// Helper for boolean view-state-backed properties
		/// </summary>
		/// <param name="name"></param>
		/// <returns></returns>
		bool? this[string name] {
			get { return (bool?)this.ViewState[name]; }
			set { this.ViewState[name] = value; }
		}

		public bool ShouldShowStateTypeSelector {
			get { return this["a"].GetValueOrDefault(true); }
			set { this["a"] = value; }
		}

		public bool ShouldAutoSelectSingleAction {
			get { return this["b"].GetValueOrDefault(true); }
			set { this["b"] = value; }
		}

		#endregion
		
		ItemState PrepareNewStateStub()
		{
			if (this.WorkflowActionPerformed) {
				
				ItemState _latestStateOfTheSameType = this.WorkflowProvider.GetLatestState(this.ParentItem, this.NewStateType) as ItemState;
				
				var _itemState =
					null != _latestStateOfTheSameType
						? _latestStateOfTheSameType.Clone(false) as ItemState
						: (ItemState)this.Definitions.CreateInstance(this.NewStateType, this.ParentItem);

				_itemState.PreviousState = this.CurrentState as ItemState;
				_itemState.Action = this.SelectedAction;
				_itemState.Definition = this.SelectedAction.LeadsTo;

				Trace.WriteLine("Created empty state stub: " + this.NewStateType.Name, "Workflow");

				return _itemState;
			} else {
				return null;
			}
		}

		protected override void CreateChildControls()
		{
			base.CreateChildControls();
			this.Controls.Clear();

			var _wf = this.WorkflowProvider.GetDefinition(this.ParentItem);

			if (null != _wf) {
				this.Controls.Add(this.itemEditorsContainer = new PlaceHolder());

				if (this.WorkflowActionPerformed) {
					
					this.InitStateEditor();
					this.UpdateItemEditor(this.PrepareNewStateStub());
					this.AddCancelActionButton();
					
				} else if (this.CanPerformSingleAction()) {
					
					this.InitStateEditor();
					this.UpdateItemEditor(this.PrepareNewStateStub());
				
				} else {
					Trace.WriteLine("Action Select", "Workflow");
					
					if (this.ShouldShowStateTypeSelector) {
						this.InitActionSelector();
					}
				}
			}
			
			this.ClearChildViewState();
		}
		
		void InitActionSelector()
		{
			var _actionQuery =
				from _action in this.CurrentStateDefinition.Items
				select new ListItem(
					HttpUtility.HtmlDecode(_action.Title),
					_action.Name);

			if (_actionQuery.Any()) {
				this.Controls.Add(this.actionSelectorContainer = new PlaceHolder());

				this.actionSelectorContainer.Controls.Add(types = new DropDownList());
				types.Items.AddRange(_actionQuery.ToArray());

				var b = new ImageButton {
					ImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(ItemEditorList), "N2.Resources.add.gif"),
					ToolTip = "Perform action",
					CausesValidation = false,
				};
				this.actionSelectorContainer.Controls.Add(b);

				b.Click += (_sender, _e) => {
					//Locate selected action by name, because DropDownList only supports items as strings
					this.SelectedAction = this.WorkflowProvider.ResolveActionForItem(
						this.ParentItem,
						types.SelectedValue) as ActionDefinition;

					//force framework to regenerate the hierarchy before rendering
					this.ChildControlsCreated = false;
				};
			}
		}

		bool CanAutoSelectSingleAction {
			get { return this.ShouldAutoSelectSingleAction && 1 == this.CurrentStateDefinition.Items.Count; }
		}

		bool CanPerformSingleAction()
		{
			if (this.CanAutoSelectSingleAction) {
				this.SelectedAction = this.CurrentStateDefinition.Items.First();
				return true;
			}
			
			return false;
		}

		void InitStateEditor()
		{
			Array.ForEach(new Control[] {
				new LiteralControl { Text = "Performing action: " },
				new Image { ImageUrl = this.SelectedAction.IconUrl },
				new LiteralControl { Text = this.SelectedAction.Title + "<br/>" },
				
				new LiteralControl { Text = "Next state: " + this.SelectedAction.LeadsTo.Title, },

				this.fs = new FieldSet {
					Legend = this.NewStateType.Name
				}
			}, itemEditorsContainer.Controls.Add);
			
			fs.Controls.Add(this.ItemEditor = new ItemEditor {
				ID = "ie"
			});

			Trace.WriteLine("Item editor created", "Workflow");
		}

		void AddCancelActionButton()
		{
			var b = new ImageButton {
				ID = "del",
				CssClass = " delete",
				ImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(ItemEditorList), "N2.Resources.delete.gif"),
				ToolTip = "Cancel Action",
				CausesValidation = false,
			};
			itemEditorsContainer.Controls.Add(b);
			
			b.Click += (_sender, _e) => {
				this.Reset();
			};
		}

		public void Reset()
		{
			this.ClearChildState();
			this.SelectedAction = null;
			this.ChildControlsCreated = false;
		}

		ItemEditor UpdateItemEditor(ContentItem item)
		{
			this.fs.Legend = this.Definitions.GetDefinition(item.GetType()).Title;
			this.ItemEditor.CurrentItem = item;
			this.ItemEditor.ParentPath = this.ParentItem.Path;
			return this.ItemEditor;
		}

		public ActionDefinition SelectedAction {
			get { return this.m_selectedActionHolder.CurrentItem as ActionDefinition; }
			set { this.m_selectedActionHolder.CurrentItem = value; }
		}

		#region IItemContainer Members

		public ContentItem ParentItem {
			get { return this.m_parentPropertyHolder.CurrentItem; }
			set { this.m_parentPropertyHolder.CurrentItem = value; }
		}

		#endregion
	}
}
